package com.qlmy.cn.api

import androidx.core.app.ComponentActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.*
import com.qlmy.cn.bean.ResultBean
import kotlinx.coroutines.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.Closeable
import java.io.File
import java.io.IOException
import kotlin.coroutines.CoroutineContext

typealias ApiError = ((Throwable) -> Unit)
typealias ApiComplete = () -> Unit

fun <T> ResultBean<T>.toData(): T? = if (isOk) (content ?: data) else throw FirmException(msg, code, content ?: data)
suspend fun String?.uploadFile(): String? = File(this).uploadFile()
suspend fun File.uploadFile(): String? {
    val uploadFileResult = SERVICE.uploadFile(MultipartBody.Builder().apply { addFormDataPart("fileImport", name, asRequestBody("multipart/form-data".toMediaType())) }.build())
    uploadFileResult.toData()
    return uploadFileResult.content
}

/**
 * 业务异常类
 */
class FirmException(override var message: String? = "", val code: Int, val data: Any?) : RuntimeException(message)

/**
 * 在OKHTTP拦截器里面只能抛该异常否则协程会无法捕获异常导致程序崩溃
 */
class InterceptorException(override var message: String? = "") : IOException(message)

/**
 * 自定义网络常类
 */
class NotNetworkException : RuntimeException("")

fun <T> ComponentActivity.launchUI(
    block: suspend ApiService.() -> T, apiObserver: Observer<ApiProcess<T>>? = null, error: ApiError? = null, complete: ApiComplete? = null
): Job = ApiRequest(lifecycleScope).launchUI(block, error, complete, apiObserver)

fun <T> Fragment.launchUI(
    block: suspend ApiService.() -> T, apiObserver: Observer<ApiProcess<T>>? = null, error: ApiError? = null, complete: ApiComplete? = null,
): Job = ApiRequest(lifecycleScope).launchUI(block, error, complete, apiObserver)

fun <T> ViewModel.launchUI(
    block: suspend ApiService.() -> T, apiObserver: Observer<ApiProcess<T>>? = null, error: ApiError? = null, complete: ApiComplete? = null
): Job = ApiRequest(viewModelScope).launchUI(block, error, complete, apiObserver)

fun <T> globalLaunchUI(
    block: suspend ApiService.() -> T, apiObserver: Observer<ApiProcess<T>>? = null, error: ApiError? = null, complete: ApiComplete? = null
): Job = ApiRequest(uiScope()).launchUI(block, error, complete, apiObserver)

/**
 * 使用类的方式调用方便以后实现重试机制
 * 调用方式
 * launchUI({网络请求},ApiObserver())
val test: ApiLiveData<String> = ApiLiveData()
 * launchUI({网络请求},test)
 */
class ApiRequest(var scope: CoroutineScope) {
    companion object {
        private const val RETRY_DELAY_MILLIS = 2000
        private const val MAX_RETRIES = 3
    }

    private val maxRetries: Int = MAX_RETRIES
    private val retryDelayMillis: Int = RETRY_DELAY_MILLIS
    fun <T> launchUI(
        block: suspend ApiService.() -> T,
        error: ApiError? = null,
        complete: ApiComplete? = null,
    ): Job = launchUI(block, error, complete, null)

    fun <T> launchUI(
        block: suspend ApiService.() -> T,
        error: ApiError? = null,
        complete: ApiComplete? = null,
        apiObserver: Observer<ApiProcess<T>>? = null
    ): Job {
        apiObserver?.onChanged(ApiProcess())
        return scope.launch(CoroutineExceptionHandler { _, throwable ->
            error?.invoke(throwable)
            apiObserver?.onChanged(ApiProcess(apiCode = ApiCode.ERROR, throwable = throwable))
            apiObserver?.onChanged(ApiProcess(apiCode = ApiCode.COMPLETE))
        }) {
            try {
                val result = SERVICE.block()
                apiObserver?.onChanged(ApiProcess(apiCode = ApiCode.SUCCESS, data = result))
            } catch (e: Throwable) {
                // 后续扩展实现重试机制
                try {
                    error?.invoke(e)
                    apiObserver?.onChanged(ApiProcess(apiCode = ApiCode.ERROR, throwable = e))
                } catch (e1: Exception) {
                    apiObserver?.onChanged(ApiProcess(apiCode = ApiCode.ERROR, throwable = e1))
                }
            }
            try {
                complete?.invoke()
                apiObserver?.onChanged(ApiProcess(ApiCode.COMPLETE))
            } catch (e1: Exception) {
                apiObserver?.onChanged(ApiProcess(ApiCode.COMPLETE))
            }
        }
    }
}

/**
 * 返回livedata方式请求接口；目前接口统一定义在ApiService；这种方式订阅者必须传递生命周期组件否则可能会内存泄漏
 * @param error 请求错误回调
 * @param complete 请求完成回调
 * @param block 请求代码块，最后一行的返回结果会传递到订阅者
 * 请求示例
 * apiLiveData { queryOrg("").toData() }.observe(this, ApiObserver(loadService = mLoadService) {
 *  TODO 自己的业务逻辑
 * })
 */
fun <T> apiLiveData(
    error: ApiError? = null,
    complete: ApiComplete? = null,
    block: suspend ApiService.() -> T,
) = liveData<ApiProcess<T>>(CoroutineExceptionHandler { _, throwable ->
    error?.invoke(throwable)
}) {
    kotlin.runCatching { emit(ApiProcess()) }
    try {
        val result: T = SERVICE.block()
        kotlin.runCatching { emit(ApiProcess(apiCode = ApiCode.SUCCESS, data = result)) }
    } catch (throwable: Throwable) {
        kotlin.runCatching { emit(ApiProcess(apiCode = ApiCode.ERROR, throwable = throwable)) }
    }
    kotlin.runCatching { complete?.invoke() }
    kotlin.runCatching { emit(ApiProcess(ApiCode.COMPLETE)) }
}

/**
 * 在非viewmodel地方使用
 */
fun uiScope() = SafeCoroutineScope(Dispatchers.Main)

class SafeCoroutineScope(context: CoroutineContext) : CoroutineScope, Closeable {
    override val coroutineContext: CoroutineContext = SupervisorJob() + context

    override fun close() {
        coroutineContext.cancelChildren()
    }
}